'''
authour  : tripod
revision : 
2022-04-05, first draft
'''
import os
import miio
import time
import datetime
import subprocess
from mlog import log
'''below enum is from https://home.miot-spec.com/spec/chunmi.health_pot.a1'''
# SIID
HEALTH_POT_SIID = 2

# properties enum, PIID mapping
CHUNMI_HEALTH_POT_PROPERTY_STATUS_PIID    = 1
CHUNMI_HEALTH_POT_PROPERTY_FAULT_PIID     = 2
CHUNMI_HEALTH_POT_PROPERTY_COOK_MODE_PIID = 3
CHUNMI_HEALTH_POT_PROPERTY_LEFT_TIME_PIID = 4

CHUNMI_HEALTH_POT_PROPERTY_STATUS    = "status"
CHUNMI_HEALTH_POT_PROPERTY_FAULT     = "fault"
CHUNMI_HEALTH_POT_PROPERTY_COOK_MODE = "cook-mode"
CHUNMI_HEALTH_POT_PROPERTY_LEFT_TIME = "left-time"

# action enum, AIID mapping
CHUNMI_HEALTH_POT_ACTIONS_START_COOK_AIID      =  1
CHUNMI_HEALTH_POT_ACTIONS_CANCEL_COOKING_AIID  =  2

CHUNMI_HEALTH_POT_ACTIONS_START_COOK     =  "start-cook"
CHUNMI_HEALTH_POT_ACTIONS_CANCEL_COOKING =  "cancel-cooking"

# status enum
CHUNMI_HEALTH_POT_STATUS_IDLE              = "Idle"
CHUNMI_HEALTH_POT_STATUS_RUNNING           = "Running"
CHUNMI_HEALTH_POT_STATUS_APTRUNNINAG       = "AptRunning"
CHUNMI_HEALTH_POT_STATUS_KEEPWARM          = "KeepWarm"
CHUNMI_HEALTH_POT_STATUS_POTOUT_IDLE       = "PotOutIdle"
CHUNMI_HEALTH_POT_STATUS_POTOUT_RUNNING    = "PotOutIdle"
CHUNMI_HEALTH_POT_STATUS_POTOUT_APTRUNNING = "PotOutRunning"
CHUNMI_HEALTH_POT_STATUS_POTOUT_KEEPWARM   = "PotOutKeepwarm"
CHUNMI_HEALTH_POT_STATUS_ERROR             = "Error"
CHUNMI_HEALTH_POT_STATUS_UPDATING          = "Updating"
CHUNMI_HEALTH_POT_STATUS_COOKFINISH        = "CookFinish"

STATUS_INDEX = \
{
    1  :CHUNMI_HEALTH_POT_STATUS_IDLE             ,
    2  :CHUNMI_HEALTH_POT_STATUS_RUNNING          ,
    3  :CHUNMI_HEALTH_POT_STATUS_APTRUNNINAG      ,
    4  :CHUNMI_HEALTH_POT_STATUS_KEEPWARM         ,
    5  :CHUNMI_HEALTH_POT_STATUS_POTOUT_IDLE      ,
    6  :CHUNMI_HEALTH_POT_STATUS_POTOUT_RUNNING   ,
    7  :CHUNMI_HEALTH_POT_STATUS_POTOUT_APTRUNNING,
    8  :CHUNMI_HEALTH_POT_STATUS_POTOUT_KEEPWARM  ,
    9  :CHUNMI_HEALTH_POT_STATUS_ERROR            ,
    10 :CHUNMI_HEALTH_POT_STATUS_UPDATING         ,
    11 :CHUNMI_HEALTH_POT_STATUS_COOKFINISH       ,
}

# fault enum
CHUNMI_HEALTH_POT_FAULT_NO_FAULT = "No Faults"
CHUNMI_HEALTH_POT_FAULT_E10      = "E10"
CHUNMI_HEALTH_POT_FAULT_E05      = "E05"
CHUNMI_HEALTH_POT_FAULT_E11      = "E11"

FAULT_INDEX = \
{
    0 : CHUNMI_HEALTH_POT_FAULT_NO_FAULT,
    1 : CHUNMI_HEALTH_POT_FAULT_E10     ,
    2 : CHUNMI_HEALTH_POT_FAULT_E05     ,
    3 : CHUNMI_HEALTH_POT_FAULT_E11     
}

# cook-mode enum
CHUNMI_HEALTH_POT_COOK_MODE_TEA        = "Tea"
CHUNMI_HEALTH_POT_COOK_MODE_FRUIT_TEA  = "Fruit Tea"
CHUNMI_HEALTH_POT_COOK_MODE_TREMELLA   = "Tremella"
CHUNMI_HEALTH_POT_COOK_MODE_SOUP       = "Soup"
CHUNMI_HEALTH_POT_COOK_MODE_BOIL_WATER = "Boil Water"
CHUNMI_HEALTH_POT_COOK_MODE_KEEP_WARM  = "Keep Warm"
CHUNMI_HEALTH_POT_COOK_MODE_POWER      = "Power"
CHUNMI_HEALTH_POT_COOK_MODE_CUSTOM     = "Custom"

CHUNMI_HEALTH_POT_COOK_MODE_TEA_VALUE        = 0  
CHUNMI_HEALTH_POT_COOK_MODE_FRUIT_TEA_VALUE  = 1
CHUNMI_HEALTH_POT_COOK_MODE_TREMELLA_VALUE   = 2
CHUNMI_HEALTH_POT_COOK_MODE_SOUP_VALUE       = 3
CHUNMI_HEALTH_POT_COOK_MODE_BOIL_WATER_VALUE = 4
CHUNMI_HEALTH_POT_COOK_MODE_KEEP_WARM_VALUE  = 5
CHUNMI_HEALTH_POT_COOK_MODE_POWER_VALUE      = 6
CHUNMI_HEALTH_POT_COOK_MODE_CUSTOM_VALUE     = 7

COOK_MODE_INDEX = \
{
    CHUNMI_HEALTH_POT_COOK_MODE_TEA_VALUE        : CHUNMI_HEALTH_POT_COOK_MODE_TEA       ,
    CHUNMI_HEALTH_POT_COOK_MODE_FRUIT_TEA_VALUE  : CHUNMI_HEALTH_POT_COOK_MODE_FRUIT_TEA ,
    CHUNMI_HEALTH_POT_COOK_MODE_TREMELLA_VALUE   : CHUNMI_HEALTH_POT_COOK_MODE_TREMELLA  ,
    CHUNMI_HEALTH_POT_COOK_MODE_SOUP_VALUE       : CHUNMI_HEALTH_POT_COOK_MODE_SOUP      ,
    CHUNMI_HEALTH_POT_COOK_MODE_BOIL_WATER_VALUE : CHUNMI_HEALTH_POT_COOK_MODE_BOIL_WATER,
    CHUNMI_HEALTH_POT_COOK_MODE_KEEP_WARM_VALUE  : CHUNMI_HEALTH_POT_COOK_MODE_KEEP_WARM ,
    CHUNMI_HEALTH_POT_COOK_MODE_POWER_VALUE      : CHUNMI_HEALTH_POT_COOK_MODE_POWER     ,
    CHUNMI_HEALTH_POT_COOK_MODE_CUSTOM_VALUE     : CHUNMI_HEALTH_POT_COOK_MODE_CUSTOM
}

CHUNMI_HEALTH_POT_ACTIONS_START_COOK      =  "start-cook"
CHUNMI_HEALTH_POT_ACTIONS_CANCEL_COOKING  =  "cancel-cooking"


''' 
User input
'''
# when any one of required mac showed in your local network, device is able to work
# require lower case
REQUIRE_DEV_MAC_LIST = \
{
    '11-22-33-44-55-66' ,
    'aa-bb-cc-dd-ee-ff' ,
}

HEALTH_POT_IP    = '192.168.1.66'
HEALTH_POT_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
WORK_TIME_GAP_SEC = 10 * 60 # 10 min
NO_WORK_TIME_GAP_SEC = 1 * 60 # 1 min
GET_UP_TIME       = '8:00'
GO_TO_SLEEP_TIME  = '22:00'
NO_WORK_AND_CANCEL_COOK = 1 * 60 * 60 # 2 hours after local server powered up, no dev found, just shutdown

loopTimeGap = 0
loopTimes = 0
leftTimeToCancel = 0


def GetStatus(value) :
    return STATUS_INDEX.get(value, 'wrong value')

def GetError(value) :
    return FAULT_INDEX.get(value, 'wrong value')

def GetCookMode(value) :
    return COOK_MODE_INDEX.get(value, 'wrong value')


def AllowedToWork(now_time) :
    # time check
    allowed = False 

    start_time = datetime.datetime.strptime(str(now_time.date()) + GET_UP_TIME,      '%Y-%m-%d%H:%M')
    end_time   = datetime.datetime.strptime(str(now_time.date()) + GO_TO_SLEEP_TIME, '%Y-%m-%d%H:%M')

    if start_time < now_time < end_time :
        allowed = True
    else :
        allowed = False

    # house phone MAC check
    if allowed == True :
        for mac in REQUIRE_DEV_MAC_LIST :
            try :
                cmd = 'arp -a | findstr '+ mac # require lower case
                returned_output = subprocess.check_output((cmd),shell=True,stderr=subprocess.STDOUT)
                parse=str(returned_output).split(' ',1)
                ip=parse[1].split(' ')
                
                log.logger.debug((str(mac) + " with ip " + str(ip) + " is found"))
                allowed = True
                break  
            except Exception :
                log.logger.debug((str(mac) + " is not found"))
                allowed = False
                pass

    return allowed

def ShutdownServer(now_time) :
    end_time   = datetime.datetime.strptime(str(now_time.date()) + GO_TO_SLEEP_TIME, '%Y-%m-%d%H:%M')

    if now_time > end_time :
        return True

    return False

def HealthPotMainFunction(miio_device, gap_sec) :
    global leftTimeToCancel

    now_time = datetime.datetime.now()

    log.logger.info(("### " + now_time.strftime("%Y-%m-%d %H:%M:%S") +" ###"))

    ableToWork = AllowedToWork(now_time)

    run_status =  miio_device.send('get_properties', [{'did': CHUNMI_HEALTH_POT_PROPERTY_STATUS, \
                                                    'piid': CHUNMI_HEALTH_POT_PROPERTY_STATUS_PIID, \
                                                    'siid': HEALTH_POT_SIID} ])[0]
    run_status =  GetStatus(run_status['value'])
    log.logger.info(("Current device status : " + str(run_status)))

    if ableToWork == True :
        leftTimeToCancel = NO_WORK_AND_CANCEL_COOK

        if run_status != CHUNMI_HEALTH_POT_STATUS_IDLE :
            left_time    =  miio_device.send('get_properties',  [{'did': CHUNMI_HEALTH_POT_PROPERTY_LEFT_TIME, \
                                                        'piid': CHUNMI_HEALTH_POT_PROPERTY_LEFT_TIME_PIID, \
                                                        'siid': HEALTH_POT_SIID} ])[0]['value']

            log.logger.info(("Left time : " + str(left_time) + " min"))
        
        if run_status == CHUNMI_HEALTH_POT_STATUS_IDLE :
            miio_device.send('action',  [{'did': CHUNMI_HEALTH_POT_ACTIONS_START_COOK, \
                                        'aiid': CHUNMI_HEALTH_POT_ACTIONS_START_COOK_AIID, \
                                        'siid': HEALTH_POT_SIID, \
                                        'in': [{'piid' : CHUNMI_HEALTH_POT_COOK_MODE_KEEP_WARM, \
                                                'value': CHUNMI_HEALTH_POT_COOK_MODE_KEEP_WARM_VALUE}]} ])
            log.logger.info("Start a new keep warm job")

    else :
        if run_status != CHUNMI_HEALTH_POT_STATUS_IDLE :
            leftTimeToCancel = leftTimeToCancel - gap_sec

            if leftTimeToCancel <= 0 :
                miio_device.send('action',  [{'did': CHUNMI_HEALTH_POT_ACTIONS_CANCEL_COOKING, \
                                'aiid': CHUNMI_HEALTH_POT_ACTIONS_CANCEL_COOKING_AIID, \
                                'siid': HEALTH_POT_SIID} ])
                log.logger.info("Cancel cooking")
            else :
                log.logger.info((str(leftTimeToCancel) + " left to cancel cooking"))
        else :
            shutdown = ShutdownServer(now_time)

            if shutdown == True :
                os.system('shutdown -s -t 30')
            

    time.sleep(gap_sec)

    return ableToWork

def __init__() :
    global loopTimeGap

    miio_device = miio.device.Device(HEALTH_POT_IP, HEALTH_POT_TOKEN)
    device_info = miio_device.info()

    log.logger.info((str(device_info) + " is founded"))

    loopTimeGap = NO_WORK_TIME_GAP_SEC

    return miio_device

def __main__(miio_device) :
    global loopTimes
    global loopTimeGap

    status = HealthPotMainFunction(miio_device, loopTimeGap)
    
    if status == True :
        loopTimeGap = WORK_TIME_GAP_SEC
    else :
        loopTimeGap = NO_WORK_TIME_GAP_SEC

    loopTimes = loopTimes + 1
    log.logger.info("loop times is " + (str(loopTimes)))


miio_device = __init__()
while True :
    __main__(miio_device)